package com.hzit.manager.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.hzit.common.utils.R;
import com.hzit.manager.client.OrderClient;
import com.hzit.manager.excep.MyException;
import io.seata.spring.annotation.GlobalTransactional;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hzit.common.utils.PageUtils;
import com.hzit.common.utils.Query;

import com.hzit.manager.dao.BrandDao;
import com.hzit.manager.entity.BrandEntity;
import com.hzit.manager.service.BrandService;


@Service("brandService")
@EnableAsync
public class BrandServiceImpl extends ServiceImpl<BrandDao, BrandEntity> implements BrandService {
	@Autowired
	private OrderClient orderClient;
	@Autowired
	private StringRedisTemplate redisTemplate;
	@Autowired
	private RedissonClient redissonClient;

	@Override
	public PageUtils queryPage(Map<String, Object> params) {
		IPage<BrandEntity> page = this.page(
				new Query<BrandEntity>().getPage(params),
				new QueryWrapper<BrandEntity>()
		);

		return new PageUtils(page);
	}

	//1. 开启全局事务 (TM TC RM)
	@GlobalTransactional
	public void saveBrandAndOrder() {
		//1.1 保存品牌
		BrandEntity brandEntity = new BrandEntity();
		brandEntity.setFirstChar("Q");
		brandEntity.setName("七匹狼");
		this.save(brandEntity);

		//1.2 保存订单
		R r = orderClient.saveOrder();
		String code = r.get("code").toString();
		if (code.equals("500")) {
			throw new MyException(code, "出现了服务调用异常！");
		}
	}

	//1. 分布式锁实现方法一：Redis + Lua脚本
	// 缺点：不能自动续期
	@Override
	public List<BrandEntity> findAll2() {

		// 生成一个随机的字符串
		String uuid = UUID.randomUUID().toString();
		// 使用redis的setNX命令为当前线程加锁
		Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 10, TimeUnit.SECONDS);
		// 如果lock是true,代表加锁成功
		if (lock) {
			List<BrandEntity> brandEntityList = null;
			//第一部分：如果加锁成功，就执行我们自己的业务逻辑
			try {
				//1.1 查询redis，如果redis中没有，就查询数据库
				String s = redisTemplate.opsForValue().get("hzit:brandList");
				
				if (StrUtil.isNotBlank(s)) {
					System.out.println("redis命中...");
					brandEntityList = JSON.parseArray(s, BrandEntity.class);
				} else {
					System.out.println("从数据库中查询...");
					brandEntityList = this.list();
					//保存到redis中
					redisTemplate.opsForValue().set("hzit:brandList", JSON.toJSONString(brandEntityList));
				}
			} finally {
				// 第二部分：最后释放哪把锁(使用lua脚本)
				String sc = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
				RedisScript<Long> script = new DefaultRedisScript(sc, Long.class);
				redisTemplate.execute(script, Arrays.asList("lock"), uuid);
			}
			return brandEntityList;
		} else {
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			return findAll2();
		}

	}

	//  2. 分布式锁实现方法二：Redisson完成
	@Override
	public List<BrandEntity> findAll3() {
		List<BrandEntity> brandEntityList = null;

		//2.1 得到分布式锁对象
		RLock lock = redissonClient.getLock("lock");
		//2.2 锁定资源
		lock.lock(10, TimeUnit.SECONDS);

		try {
			//处理自己的业务：查询redis，如果redis中没有，就查询数据库
			String s = redisTemplate.opsForValue().get("hzit:brandList2");
			if (StrUtil.isNotBlank(s)) {
				System.out.println("redis命中2...");
				brandEntityList = JSON.parseArray(s, BrandEntity.class);
			} else {
				System.out.println("从数据库中查询2...");
				brandEntityList = this.list();
				//保存到redis中
				redisTemplate.opsForValue().set("hzit:brandList2", JSON.toJSONString(brandEntityList));
			}
			return brandEntityList;
		} finally {
			// 2.3 释放哪把锁
			lock.unlock();
		}
	}
}